#include "net.h"
#include "log.h"
#include "format.h"
#include "rsa.h"
#include "utility.h"
#include "prompt.h"

#include <assert.h>
#include <pthread.h>
#include <stdlib.h>


//心跳包频率,6s
static const struct timeval heartbeat = {6,0};


/**
 * @brief  一个新客户端连接上服务器了将调用此函数
 * @note   当此函数被调用时，libevent已经帮我们accept了这个客户端。该客户端的
 * @param  *listener: 
 * @param  fd: 文件描述符为fd
 * @param  *sock: 
 * @param  socklen: 
 * @param  *arg: 
 * @retval None
 */
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,    
                 struct sockaddr *cliAddr, int socklen, void *arg)    
{
    printf("connection from %s,port:%d,sockfd:%d\n",
            inet_ntoa(((struct sockaddr_in *)cliAddr)->sin_addr),
            ntohs(((struct sockaddr_in *)cliAddr)->sin_port),
            fd);
    add_log("connection from %s,port:%d,sockfd:%d\n",
            inet_ntoa(((struct sockaddr_in *)cliAddr)->sin_addr),
            ntohs(((struct sockaddr_in *)cliAddr)->sin_port),
            fd);

    struct event_base *base = (struct event_base*)arg;
    //为这个客户端分配一个 bufferevent，释放时关闭套接字，使用锁会失败,为什么啊啊啊？？
    struct bufferevent *bev =  bufferevent_socket_new(base, fd,BEV_OPT_CLOSE_ON_FREE);
    assert(bev !=NULL);
    bufferevent_setwatermark(bev,EV_READ,6,1024);//调整水位
    bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL);
    bufferevent_enable(bev, EV_READ | EV_PERSIST);
    bufferevent_set_timeouts (bev, &heartbeat, NULL);
}

void socket_event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF){
        printf("connection closed\n");
    }else if (events & BEV_EVENT_ERROR){
        printf("some other error\n");
    }else if (events & BEV_EVENT_CONNECTED) {
        /* 业务操作，当连接成功是回调*/
        printf("BEV_EVENT_CONNECTED\n");
    }else if (events & BEV_EVENT_TIMEOUT) {
        /* 注意，若触发了timeout事件，那么read事件会被disable，此时若想继续运行，需要重新enable read事件*/
        printf("BEV_EVENT_TIMEOUT\n");
        //struct bufferevent* evClient = (struct bufferevent*)bev;
        //bufferevent_enable(evClient, EV_READ);
        //bufferevent_write(bev, "$keep alive;", strlen("$keep alive;\n"));
    }

    //这将自动close套接字和free读写缓冲区
    del_user(bev);
}


void *socket_read_cb_thread(void *arg)
{
    struct bufferevent *bev = arg;
    //ssize_t len = bufferevent_read(bev, msg, Msg.m_msgLen );
    char buf[MAX_MESSAGE_SIZE];
    bzero(buf,MAX_MESSAGE_SIZE);
    int count=0;

    count = bufferevent_read(bev, buf, Msg.m_msgLen );
    /* 1.对端发送FIN后，还向这个套接字写会收到 RST */
    if( count < 0){
        perror("read sockfd");
        del_user(bev);  //关掉这个套接字
        return NULL;
    }
    /* 2.对方发送了FIN,服务器读会返回0，应答后处于CLOSE_WAIT状态 */
    else if (count == 0){
        printf("\ra user closed  \n");
        del_user(bev);  //关掉这个套接字
        return NULL;
    }
    /* 3.没有读到6个字节 */
    else if(count < 6){
        printf("messages is less than 6\n");
        del_user(bev);  //关掉这个套接字
        return NULL;
    }

    /* 4.正常读数据 */
    unsigned short cmd_num = 0;
    unsigned int packet_len = 0;
    head_analyze(buf,&cmd_num,&packet_len);
    count = bufferevent_read(bev, buf, packet_len);
    if(count < packet_len){
        printf("cmd_num = %d,count = %d\n,packet_len = %d\n",cmd_num,count,packet_len);
        printf("read failed!??\n");
        return NULL;
    }
    delServerRecv(bev,cmd_num,packet_len,buf);
    return NULL;
}
/**
 * @brief  接收到客户端数据时调用此函数
 * @note   
 * @param  *bev: 
 * @param  *arg: 
 * @retval None
 */
void socket_read_cb(struct bufferevent *bev, void *arg)
{
    pthread_t read_tid;
    int err = pthread_create(&read_tid,NULL,socket_read_cb_thread,bev);
    assert(err == 0);
}
/** 
 * @brief  处理服务端接收的套接字数据
 * @note   
 * @param  cmd: 
 * @param  packet_len: 
 * @param  *buf: 
 * @retval 
 */
int delServerRecv(struct bufferevent *bev,u_int16_t cmd,u_int32_t packet_len,char *buf)
{
    signed int err = 0;
    char send_cmd[MAX_MESSAGE_SIZE];
    //printf("cmd num = %d\n",cmd);
    switch (cmd){
        case e_msgDebug:
            printf("recv: %s",buf);
            break;
        case e_msgLogin:
             //登录
             //printf("\rlogin request\ncommand>");
             err = user_confirmation(bev,buf,packet_len);    //ref g_bank_data
             break;
        case e_msgTimestamp:
            //printf("serv: e_msgTimestamp\n");
            err = check_timestamp(bev,buf,packet_len);
            break;
        case e_msgBegTimestamp:
            err = return_timestamp(bev,buf,packet_len);
            break;
        default:
            printf("unknow cmd\n");
            return -1;
    }
    if(err){
        head_package(send_cmd,err,0);
        bufferevent_write(bev, send_cmd, Msg.m_msgLen);
    }
    return 0;
}
int return_timestamp(struct bufferevent *bev,void *buf,u_int32_t packet_len)
{
    char send_cmd[256];
    char *name =find_name_byBev(bev);
    u_int32_t pos =-1;
    if(name !=NULL)
        pos = find_pen_pos_byName(name);
    int64_t timestamp = get_pen_timestamp(pos);
    int len = encodeTimestamp(send_cmd+6,pos,timestamp);//先不检查返回值
    head_package(send_cmd,e_msgTimestamp,len);
    bufferevent_write(bev, send_cmd, Msg.m_msgLen+len);
    //printf("name = %s,pos = %d,send:%lu\n",name,pos,timestamp);
    return 0;
}
int check_timestamp(struct bufferevent *bev,void *buf,u_int32_t packet_len)
{
    u_int32_t position;
    int64_t timestamp;
    decodeTimestamp(buf,packet_len,&position,&timestamp);
    //printf("position:%d,time:%s\n",position,ctime((time_t *)&timestamp));
    //struct tm *p_tm = localtime(&timestamp); /*获取GMT时间*/
    //printf("recv:%d-%d-%d %d:%d:%d --> ", (p_tm->tm_year+1900), (p_tm->tm_mon+1), p_tm->tm_mday,p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec);
    int64_t old_time = gettimestamp(position);

    //time_t now = time(NULL);
    //printf("recv:%lu,old:%lu,now:%lu\n",timestamp,old_time,now);

    //p_tm = localtime(&old_time);
    //printf("old:%d-%d-%d %d:%d:%d\n ", (p_tm->tm_year+1900), (p_tm->tm_mon+1), p_tm->tm_mday,p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec);
    //p_tm = localtime(&now);
    //printf("old:%d-%d-%d %d:%d:%d\n ", (p_tm->tm_year+1900), (p_tm->tm_mon+1), p_tm->tm_mday,p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec);

    if((timestamp > old_time && (abs(timestamp - old_time) < 100)) || (timestamp>=40&&timestamp<=200)){
        set_study_status(position,1);
        //printf("studying...........\n");
        printf("heart beat: %lu\n",timestamp<=200?timestamp:abs(timestamp - old_time));
    }else{
        set_study_status(position,0);
        printf("resting...........\n");
    }
    send_update_timestamp(bev,position);
    return 0;
}
int user_confirmation(struct bufferevent *bev,void *buf,u_int32_t packet_len)
{
    /* 1.解密登陆信息 */
    int *encoded = buf;
    char *decoded = decodeMessage(packet_len/sizeof(int), 
            key.m_private.m_bytes, 
            encoded, key.m_private.m_exponent, 
            key.m_private.m_modulus);
    //printf("decode = %s\n",decoded);
    char name[128],passwd[128];
    char *p1=NULL,*p2=NULL,*p3=NULL;
    p1 = strstr(decoded,"$");
    if(p1!=NULL)
        p2=strstr(p1+1,";");
    if(p2!=NULL)
        p3=strstr(p2+1,";");
    if(p3==NULL)
        return e_msgWrongPasswd;
    *(p2)='\0';
    *(p3)='\0';
    memcpy(name,p1+1,p2-p1-1);
    memcpy(passwd,p2+1,p3-p2-1);
    //printf("name = %s\n",name);
    //printf("passwd = %s\n",passwd);
    int PC_PEN_flag;
    if(p3[2]=='c')
        PC_PEN_flag = E_PC;
    else
        PC_PEN_flag = E_PEN;
    /* 2.从数据库中查找用户 */
    //...没找到就直接退出了，现在暂时假设找到了

    /* 3.给用户返回时间戳 */
    u_int32_t position = add_user(bev,name,PC_PEN_flag);
    if(PC_PEN_flag == E_PEN)
        send_update_timestamp(bev,position);
    free(decoded);
    return 0;
    //return e_msgWrongPasswd;
}
void send_update_timestamp(struct bufferevent *bev,u_int32_t position)
{
    update_timestamp(position);
    int64_t timestamp = gettimestamp(position);
    char send_cmd[256];
    int len = encodeTimestamp(send_cmd+6,position,timestamp);//先不检查返回值
    head_package(send_cmd,e_msgTimestamp,len);
    bufferevent_write(bev, send_cmd, Msg.m_msgLen+len);

    //printf("position:%d,num:%lu,time:%s\n",position,timestamp,ctime((time_t *)&timestamp));
    //decodeTimestamp(send_cmd+6,len,&position,&timestamp);
    //printf("position:%d,num:%lu,time:%s\n",position,timestamp,ctime((time_t *)&timestamp));
}